/**
 * \file    parser.h
 * \brief   Parser generator.
 * \author  Jérôme LAURENS
 * \version 0.1
 * \date    September 17, 2014
 *
 * Reads a LALR(1) grammar, generates a parser based on a template file.
 * This is based on lemon parser generator, but has strongly been
 * revisited.
 *
 */

#ifndef PARSER_H
#define PARSER_H

#include <stack>
#include <fstream>
#include <sstream>
#include <iostream>
#include "common.h"
#include "grammar.h"
#include "rule.h"
#include "symbol.h"
#include "item.h"

LPP_NAMESPACE_BEGIN

/**
 * @class Parser
 * @brief The Parser class
 *
 * This class reads a text file describing a grammar
 * and constructs a grammar object.
 */
class Parser {
public:
    typedef Parser __self;
    /**
     * @brief Parser
     *
     * Designated constructor of the class.
     *
     * @param __f: The file name where the grammar is described.
     * @param grammar: an input grammar to be completed.
     * Throw std::logic_error if no grammar is given.
     * @param defines: the macros defined from the command line.
     *
     */
    Parser(String const & __f
         , GrammarRef const & grammar
         , StringSet & defines)
            : defines_m(defines)
            , file_name_m(__f)
            , line_number_m(0)
            , number_of_errors_m(0)
            , precedence_m(0) {
        if (!grammar) {
            throw std::logic_error("Unexpected void grammar reference.");
        }
        std::ifstream * p = new std::ifstream(__f);
        input_m.reset(p);
        if (!p->is_open()) {
            error()<<"Can't open this file for reading."<<std::endl;
        }
    }
    /**
     * @brief Parser
     *
     * Convenient constructor for unit testing.
     *
     * @param __f: an input file name (unused during unit testing)
     * @param grammar: an input grammar to be completed.
     * Throw std::logic_error if no grammar is given.
     * @param __s: an input string stream
     * @param defines: the macros defined from the command line.
     */
    Parser(String const & __f
         , GrammarRef const & grammar
         , String const & __s
         , StringSet & defines)
            : defines_m(defines)
            , file_name_m(__f)
            , line_number_m(0)
            , number_of_errors_m(0)
            , precedence_m(0)
            , grammar_m(grammar) {
        input_m.reset(new std::istringstream(__s));
        if (!grammar) {
            throw std::logic_error("Unexpected void grammar reference.");
        }
        parse();
    }
    /**
     * @brief parse
     *
     * Parse the grammar file.
     *
     * @return the number of errors encontered while parsing.
     */
    Index parse();

    /**
     * @brief Number of errors.
     *
     * @return the number of errors encontered while parsing.
     */
    Index number_of_errors() const {
        return number_of_errors_m;
    }

    /**
     * @brief retrieve the grammar constructed. Run parse() before.
     * @return the grammar object. It is complete when the parse() method
     * has return true, otherwise its status is unspecified.
     * The file is parsed line by line through the use of a string stream.
     * The parser work is based on a LIFO queue of messages,
     * which are just a member function paired with the number
     * of the current line being parsed.
     *
     * As long as the queue is not empty, the parse(...) method pops the top
     * message and executes it. The very first message is a
     * get_next_filtered_if_any_or_pop, the next one being a feed_action_stack.
     * If the stream is void, the feed_action_stack is executed otherwise it is
     * skipped.
     *
     * The messages are added to the queue depending on the line
     * parsed. The branching is made through regular expressions matching.
     */
    GrammarRef const & grammar() const {
        return grammar_m;
    }

#if 0
    ItemList Item const items() const {
        return items_m;
    }
#endif
    ItemList items_m;
private:
    /**
     * @brief All the member variables are private.
     */

    StringSet & defines_m;               /**< The macros defined on the command line. */
    String const & file_name_m;          /**< The file containing the grammar description. */
    std::unique_ptr<std::istream> input_m;
                                    /**< The file stream with the contents of the text file. */
    Index line_number_m;            /**< The number of the currently parsed line. */
    Index number_of_errors_m;       /**< The total number of errors so far. */
    Index precedence_m;       /**< Precedence level, incremented by one for each precedence line. */
    GrammarRef grammar_m;           /**< The grammar to be constructed. */

private:
    static const String special_str;
    static const String macro_str;
    static const String terminal_str;
    static const String or_terminal_str;
    static const String non_terminal_str;
    static const String symbol_str;
    static const String alias_str;
    static const String post_precedence_str;
    static const String post_associativity_str;

public:
    static const String here_str;
    static const String key_str;
    static const Regex comment_rx;
    static const Regex ifdef_rx;
    static const Regex ifndef_rx;
    static const Regex ifdef_or_ifndef_rx;
    static const Regex else_rx;
    static const Regex endif_rx;
    static const Regex define_rx;
    static const Regex undef_rx;
    static const Regex macro_rx;
    static const Regex shortcut_rx;
    static const Regex wildcard_rx;
    static const Regex start_rx;
    static const Regex prec_rx;
    static const Regex left_rx;
    static const Regex right_rx;
    static const Regex nonassoc_rx;
    static const Regex fallback_rx;
    static const Regex continuation_rx;
    static const Regex terminal_rx;
    static const Regex non_terminal_rx;
    static const Regex lhs_rx;
    static const Regex non_lhs_rx;
    static const Regex rhs_rx;
    static const Regex rhs_multi_rx;
    static const Regex more_rhs_rx;
    static const Regex terminal_or_key_rx;
    static const Regex more_terminal_or_key_rx;
    static const Regex key_rx;
    static const Regex sharp_rx;
    static const Regex cpp_comment_rx;
    static const Regex cpp_comment_or_eol_rx;
    static const Regex class_rx;
    static const Regex equals_rx;
    static const Regex or_terminal_or_key_rx;
    static const Regex more_rx;
    static const Regex precedence_rx;
    static const Regex post_precedence_rx;
    static const Regex post_associativity_rx;

    std::ostream & error(Index const line_number) {
        return std::cerr << file_name_m << " error #"<< ++number_of_errors_m << " at line " << line_number << ": ";
    }
    std::ostream & error() {
        return error(line_number_m);
    }
    String escaped_string_4_regex(String const & __s);

private:
    typedef std::function<bool()> Action;
    typedef std::pair<Action,Index> ActionIndex;
    typedef std::stack<ActionIndex> ActionIndexQ;
    typedef std::stack<bool>        YorNQ;

    ActionIndexQ if_actions_s;      /**< The stack of the %if actions. */
    ActionIndex if_action;          /**< The current action. */
    ActionIndexQ actions_s;         /**< The stack of the parsing actions. */
    ActionIndex action;             /**< The current action. */
    String line;
    String symbol, alias;
    String::const_iterator begin_m, end_m;
    std::smatch result_m;

    RuleRef rule_m;
    SymbolRef symbol_m;
    bool should_translate_m = false;

    Symbol::Associativity::Value associativity_m;

    bool get_next_line();
    void feed_action_stack();

    bool proceed_shortcut_if_any();
    bool proceed_wildcard_if_any();
    bool proceed_start_if_any();

    bool proceed_precedence_if_any();
    bool require_precedence_continued();
    bool complete_precedence();

    bool proceed_other_if_any();
    bool proceed_other();

    bool proceed_class_if_any();
    bool complete_class_rhs();
    bool require_class_rhs();
    bool require_class_rhs_continued();
    bool complete_class_more_rhs();

    bool proceed_fallback_if_any();
    bool require_fallback_rhs();
    bool require_fallback_rhs_continued();
    bool complete_fallback_more_rhs();

    bool proceed();
    bool preprocess();
    bool require_endif();
    bool preprocess_since_else_until_required_endif();
    bool preprocess_until_else_or_required_endif();
    bool preprocess_until_required_endif();
    bool proceed_if_controls();

    bool proceed_reduction_if_any();
    bool proceed_reduction_template_code_if_any();

    bool complete_reduction_rhs();
    bool complete_reduction_rhs_multi();
    bool complete_reduction_rhs_if_any();
    bool require_reduction_more_rhs();
    bool require_reduction_more_rhs_continued();

    bool complete_post_associativity_if_any(SymbolRef & S);
    bool complete_post_precedence_if_any(SymbolRef & S);

    bool match(const std::regex & rx);
    String whole_match();
    String first_match();
    String second_match();
    String third_match();
    String symbol_match();
    bool match_precedence(Symbol::Associativity::Value &associativity);

    bool setup_accept_rule();
    bool accept_non_terminals();
    bool sort_symbols();

};

LPP_NAMESPACE_END

#endif // PARSER_H
